#pragma once
#include "Common.hpp"
#include <pthread.h>
#include <sys/wait.h>
#include <functional>
#include "ThreadPool.hpp"
#include "Command.hpp"

using namespace ThreadPoolModule;
using task_t =std::function<void()>;
using func_t=std::function<std::string(const std::string&word,InetAddr&peer)>;
// using func_t=std::function<std::string(const std::string&)>;

const static int GlabolBacklog=8;
const static int Globalsockfd=-1;

class TCPServe : public NoCopy
{
public:
    TCPServe(uint16_t port,func_t func)
        :_port(port),
         _listensockfd(Globalsockfd),
         _isrunning(false),
         _func(func)
    {}
    void Init()
    {
        // 1.创建socket
        _listensockfd=socket(AF_INET,SOCK_STREAM,0);
        if(_listensockfd < 0)
        {
            LOG(FATAL)<<"socket create error";
            exit(SOCKET_ERR);
        }
        LOG(INFO)<<"socket create success "<<_listensockfd;
        
        // 2.绑定socket，InetAddr
        InetAddr local(_port);
        int m=bind(_listensockfd,local.CompulsoryNetAddr(),local.AddrLen());
        if(m<0)
        {
            LOG(FATAL)<<"bind error";
            exit(BIND_ERR);
        }
        LOG(FATAL)<<"bind success "<<_listensockfd;
        
        // 3.设置状态为listen
        m=listen(_listensockfd,GlabolBacklog);
        if(m<0)
        {
            LOG(FATAL)<<"listen error";
            exit(LISTEN_ERR);
        }
        LOG(FATAL)<<"listen success "<<_listensockfd;

    }

    void Service(int sockfd,InetAddr peer)
    {
        char buffer[1024];
        while(true)
        {
            ssize_t n = read(sockfd,buffer,sizeof(buffer)-1);
            if(n==0)
            {
                LOG(INFO)<<peer.StringAddr()<<" 退出了"<<buffer;
                LOG(DEBUG)<<"请检查输入是否为空或者写端关闭";
                close(sockfd);
                break;
            }
            else if(n<0)
            {
                LOG(INFO)<<peer.StringAddr()<<" 异常"<<buffer;
                close(sockfd);
                break;
            }
            else
            {
                buffer[n]=0;
                LOG(INFO)<<peer.StringAddr()<<" say# "<<buffer;
                std::string serve=_func(buffer,peer);
                serve+="\t";
                serve+=buffer;
                write(sockfd,serve.c_str(),serve.size());
            }

        }
    }
    

    class ThreadData
    {
    public:
        ThreadData(int sockfd,InetAddr &addr,TCPServe* s)
            :_socketfd(sockfd),
             _addr(addr),
             tsvr(s)
        {}
    public:
        int _socketfd;
        InetAddr _addr;
        TCPServe* tsvr;
    };


    static void *Routine(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* td=static_cast<ThreadData*>(args);
        td->tsvr->Service(td->_socketfd,td->_addr);
        delete td;
        return nullptr;
    }

    void Start()
    {
        _isrunning=true;
        while(_isrunning)
        {
            //获取连接
            struct sockaddr_in peer;
            socklen_t len=sizeof(sockaddr_in);//socksddr_in
            int socketfd=accept(_listensockfd,(struct sockaddr*)&peer,&len);
            if(socketfd<0)
            {
                LOG(WARNING)<<"accept error";
                continue;
            }       
            InetAddr addr(peer);
            LOG(INFO)<<"accpet success "<<addr.StringAddr();

            //目前只一个单进程程序，一次只能一个建立连接，下一个想建立连接必须等前一个退出链接才能连接成功
            //这种程序没什么用，下面引入多进程

            //多进程版本
            //1.创建进程
            // pid_t id=fork();//父进程
            // if(id<0)
            // {
            //     LOG(FATAL)<<"fork error";
            //     exit(FORK_ERR);
            // }
            // //子进程
            // else if(id==0)
            // {
            //     //关闭掉不需要的文件描述符
            //     close(_listensockfd);
            //     //当前程序一次只能waitpid一个，在waitpid下一个子进程的时间内，它是阻塞的，程序不能执行下去
            //     //所以要执行下面这个操作，父进程退出后，OS会自动回收子进程
            //     //还可以解决的办法
            //     //1.使用signal设置SIGCHILD的默认处理方式为SIG_IGN,
            //     //2.将waitpid的第三个参数设置为WNOHANG，非阻塞状态
            //     if(fork()>0)//子进程的子进程，创建成功，子进程退出，
            //         exit(OK);
            //     //任务应该交给子进程处理
            //     Service(socketfd,addr);
            //     exit(OK);
            // }
            // //父进程
            // else
            // {
            //     //关闭掉不需要的文件描述符
            //     close(socketfd);
            //     //父进程应该正确处理子进程的退出状态
            //     pid_t pid=waitpid(id,nullptr,0);
            //     (void)pid;                
            // }

            //多线程版本,不知道为啥一直抛new异常
            // ThreadData* td=new ThreadData(socketfd,addr,this);
            // pthread_t tid;

            // //传参要慎重再慎重，td传成二级指针了，一直抛new异常，找了一个多小时，现在修正了
            // pthread_create(&tid,nullptr,Routine,td);
            
            //另外两种解决办法
            //1.
            // if(pthread_create(&tid,nullptr,Routine,td)!=0)
            // {
            //     LOG(ERROR) << "pthread_create failed";
            //     delete td;
            //     close(socketfd);
            //     continue;
            // }  
            // pthread_detach(pthread_self());

            //2.
            // std::shared_ptr<ThreadData> td = std::make_shared<ThreadData>(socketfd, addr, this);
            // pthread_t pid;
            // // 创建线程，传递智能指针的复制
            // if(pthread_create(&pid, nullptr, 
            //     [](void* arg) -> void* {
            //         auto td = *static_cast<std::shared_ptr<ThreadData>*>(arg);
            //         td->tsvr->Service(td->_socketfd, td->_addr);
            //         return nullptr;
            //     }, 
            //     new std::shared_ptr<ThreadData>(td)) != 0)
            // {
            //     LOG(ERROR) << "pthread_create failed";
            //     close(socketfd);
            //     continue;
            // }
            // pthread_detach(pid);


            //线程池版本：
            ThreadPool<task_t>::GetInstance()->EnterQueue([this,socketfd,&addr](){
                this->Service(socketfd,addr);
            });
            
        }
        _isrunning=false;
    }
    ~TCPServe(){}
private:
    int _listensockfd;
    uint16_t _port;
    bool _isrunning;
    func_t _func;
};
